package com.moons.demo.aicamera.facerecognition.util;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Rect;
import android.graphics.drawable.BitmapDrawable;
import android.text.InputType;
import android.text.TextUtils;
import android.util.Log;
import android.widget.Toast;

import com.afollestad.materialdialogs.MaterialDialog;
import com.moons.demo.aicamera.facerecognition.FrameCacheUtil;
import com.moons.demo.aicamera.facerecognition.config.DemoConfig;
import com.moons.demo.aicamera.facerecognition.constants.Common;
import com.moons.demo.aicamera.facerecognition.model.UserFace;
import com.xqd.sdk.libaicamera.AICamera;
import com.xqd.sdk.libaicamera.face.FaceAttribute;
import com.xqd.sdk.libaicamera.face.FaceObject;
import com.xqd.sdk.libaicamera.face.FaceRegister;
import com.xqd.sdk.libaicamera.utils.Utils;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;

import io.reactivex.Observable;
import io.reactivex.ObservableEmitter;
import io.reactivex.ObservableOnSubscribe;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.annotations.NonNull;
import io.reactivex.functions.Consumer;
import io.reactivex.schedulers.Schedulers;

import static com.moons.demo.aicamera.facerecognition.util.FaceDataUtil.byteArr2floatArr;
import static com.moons.demo.aicamera.facerecognition.util.FaceDataUtil.floatArr2byteArr;

public class RegisterHelper {
    private static final String TAG = "RegisterHelper";
    MaterialDialog materialDialog;

    private RegisterHelper() {
    }

    public static RegisterHelper getInstance() {
        return RegisterHelperHolder.INSTANCE;
    }

    private static class RegisterHelperHolder {
        private static final RegisterHelper INSTANCE = new RegisterHelper();
    }


    public void registFace(final Activity activity, final long faceFrameId, final FaceObject object) {
        Log.d(TAG, "registFace: id=" + object.id());
        Log.d(TAG, "registFace: faceId=" + faceFrameId);


        final float[] features = Arrays.copyOf(object.mFeatures, object.mFeatures.length);
        //获取到特征值，需要先判断此特征值在数据库中有没有相似度大于40%的，如有，则认为是同一个人。

        UserFace userFace = RegisterHelper.getInstance().getRegisteredInfo(features, features.length);

        if (userFace != null) {

            final String msg = String.format(Locale.getDefault(), "该用户( %s )已经注册,相似度为 %.4f", userFace.getName(), userFace.getSimilarity());

            activity.runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    Toast.makeText(activity, msg, Toast.LENGTH_SHORT).show();
                }
            });


        } else {
            //如果没注册，再去缓存的图像帧中找相对于的帧号图像数据
            Iterator<byte[]> iterator = FrameCacheUtil.getInstance().frameDataCacheQueue.iterator();
            while (iterator.hasNext()) {
                byte[] frameData = iterator.next();
                if (frameData == null) {
                    break;
                }

                long id = Utils.getIdFromFrameData(frameData);
                Log.d(TAG, "registFace: id in queue =" + id);
                if (faceFrameId < id) {
                    break;
                } else if (faceFrameId == id) {
                    if (object.mFeatures == null) {
                        Log.d(TAG, "registFace: object.mFeatures==null");
                        return;
                    }

                    Log.i(TAG, "registFace: find match frame");
                    Rect faceRect = object.rect();

                    Bitmap bitmap = BitmapFactory.decodeByteArray(frameData, 0, frameData.length);

                    if (bitmap != null) {
                        if (bitmap.getHeight() >= faceRect.height() && bitmap.getWidth() >= faceRect.width()) {

                            final Bitmap faceBitmap = Bitmap.createBitmap(bitmap, faceRect.left, faceRect.top, faceRect.width(), faceRect.height());

                            if (materialDialog != null && materialDialog.isShowing()) {
                                Log.d(TAG, "onFaceRegisterSuccess: dialog is showing!  return.");
                                return;
                            }

                            activity.runOnUiThread(new Runnable() {
                                @Override
                                public void run() {

                                    materialDialog = new MaterialDialog.Builder(activity)
                                            .title("请输入姓名:")
                                            .icon(new BitmapDrawable(faceBitmap))
                                            .inputType(InputType.TYPE_CLASS_TEXT)
                                            .input("张三", "", new MaterialDialog.InputCallback() {
                                                @Override
                                                public void onInput(MaterialDialog dialog, CharSequence input) {
                                                    // Do something
                                                    if (!TextUtils.isEmpty(input)) {

                                                        UserFace userFace = new UserFace();

                                                        userFace.setAge(object.mAttribute.mAge);
                                                        if (object.mAttribute.mGender == FaceAttribute.Gender.MALE) {

                                                            userFace.setGerder(Common.Genger.MALE);

                                                        } else if (object.mAttribute.mGender == FaceAttribute.Gender.FEMALE) {
                                                            userFace.setGerder(Common.Genger.FEMALE);
                                                        } else {
                                                            userFace.setGerder(Common.Genger.UNKNOWN);
                                                        }

                                                        userFace.setName(input.toString());
                                                        userFace.setFeature(floatArr2byteArr(features));

                                                        long id = FaceDataUtil.getInstance().insert(userFace);
                                                        if (id == -1) {
                                                            Toast.makeText(activity, "人脸注册失败:数据库错误", Toast.LENGTH_SHORT).show();
                                                        } else {
                                                            userFace.setPictureId(id);
                                                            FaceDataUtil.getInstance().update(userFace);
                                                            synchronized (FaceDataUtil.userFaces_all) {
                                                                FaceDataUtil.userFaces_all.add(userFace);
                                                            }
                                                            File file = new File(DemoConfig.FacePicPath + id + ".jpg");
                                                            FaceDataUtil.SaveBitmap(faceBitmap, file);
                                                            Toast.makeText(activity, "注册成功，人脸数据已保存在数据库:" + DemoConfig.DatabasePath + "  路径下。", Toast.LENGTH_LONG).show();
                                                        }

                                                    } else {
                                                        Toast.makeText(activity, "请输入姓名！", Toast.LENGTH_SHORT).show();
                                                    }


                                                }
                                            }).show();
                                }
                            });

                        }


                    }


                    break;
                }


            }
        }
    }

    @SuppressLint("CheckResult")
    public void registFace(final Activity activity, final AICamera aiCamera, final Bitmap bitmap) {

        if (aiCamera == null || bitmap == null) {
            return;
        }

        Observable.create(new ObservableOnSubscribe<FaceObject>() {
            @Override
            public void subscribe(@NonNull ObservableEmitter<FaceObject> emitter) {

                FaceRegister faceRegister = aiCamera.getFaceRegister(activity.getApplicationContext());
                if (faceRegister != null) {

                    ByteArrayOutputStream baos = new ByteArrayOutputStream();
                    Log.d(TAG, "run: Height()=" + bitmap.getHeight());
                    Log.d(TAG, "run: getWidth()=" + bitmap.getWidth());
                    bitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos);
                    byte[] data = baos.toByteArray();

                    List<FaceObject> faceObjectList = faceRegister.registerFromJpeg(data);

                    if (faceObjectList == null) {
                        Log.d(TAG, "subscribe: faceObjectList==null");
                    }

                    if (faceObjectList != null && !faceObjectList.isEmpty()) {

                        for (FaceObject faceObject : faceObjectList) {

                            emitter.onNext(faceObject);
                            break;
                            //演示用，仅注册第一个识别到的人脸
                        }

                    }

                }

            }
        }).subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Consumer<FaceObject>() {
                    @Override
                    public void accept(final FaceObject faceObject) {

                        Log.d(TAG, "onFaceRegisterSuccess!");

                        Rect rect = faceObject.rect();
                        final float[] features = Arrays.copyOf(faceObject.mFeatures, faceObject.mFeatures.length);
                        Log.d(TAG, "onFaceRegisterSuccess: features.length=" + features.length);
                        //获取到特征值，需要先判断此特征值在数据库中有没有相似度大于40%的，如有，则认为是同一个人。

                        UserFace userFace = RegisterHelper.getInstance().getRegisteredInfo(features, features.length);
                        if (userFace != null) {

                            String msg = String.format(Locale.getDefault(), "该用户( %s )已经注册,相似度为 %.4f", userFace.getName(), userFace.getSimilarity());

                            Toast.makeText(activity, msg, Toast.LENGTH_SHORT).show();

                        } else {

                            if (bitmap.getHeight() >= rect.height() && bitmap.getWidth() >= rect.width()) {

                                final Bitmap faceBitmap = Bitmap.createBitmap(bitmap, rect.left, rect.top, rect.width(), rect.height());


                                if (materialDialog != null && materialDialog.isShowing()) {
                                    Log.d(TAG, "onFaceRegisterSuccess: dialog is showing!  return.");
                                    return;
                                }
                                materialDialog = new MaterialDialog.Builder(activity)
                                        .title("请输入姓名:")
                                        .icon(new BitmapDrawable(faceBitmap))
                                        .inputType(InputType.TYPE_CLASS_TEXT)
                                        .input("张三", "", new MaterialDialog.InputCallback() {
                                            @Override
                                            public void onInput(MaterialDialog dialog, CharSequence input) {
                                                // Do something
                                                if (!TextUtils.isEmpty(input)) {

                                                    UserFace userFace = new UserFace();
                                                    userFace.setName(input.toString());
                                                    userFace.setFeature(floatArr2byteArr(features));

                                                    userFace.setAge(faceObject.attribute().mAge);
                                                    if (faceObject.mAttribute.mGender == FaceAttribute.Gender.MALE) {
                                                        userFace.setGerder(Common.Genger.MALE);
                                                    } else if (faceObject.mAttribute.mGender == FaceAttribute.Gender.FEMALE) {
                                                        userFace.setGerder(Common.Genger.FEMALE);
                                                    } else {
                                                        userFace.setGerder(Common.Genger.UNKNOWN);
                                                    }

                                                    long id = FaceDataUtil.getInstance().insert(userFace);
                                                    if (id == -1) {
                                                        Toast.makeText(activity, "人脸注册失败:数据库错误", Toast.LENGTH_SHORT).show();
                                                    } else {
                                                        userFace.setPictureId(id);
                                                        FaceDataUtil.getInstance().update(userFace);
                                                        synchronized (FaceDataUtil.userFaces_all) {
                                                            FaceDataUtil.userFaces_all.add(userFace);
                                                        }
                                                        File file = new File(DemoConfig.FacePicPath + id + ".jpg");
                                                        FaceDataUtil.SaveBitmap(faceBitmap, file);
                                                        Toast.makeText(activity, "注册成功，人脸数据已保存在数据库:" + DemoConfig.DatabasePath + "  路径下。", Toast.LENGTH_LONG).show();
                                                    }

                                                } else {
                                                    Toast.makeText(activity, "请输入姓名！", Toast.LENGTH_SHORT).show();
                                                }


                                            }
                                        }).show();


                            }
                        }

                    }
                }, new Consumer<Throwable>() {
                    @Override
                    public void accept(Throwable throwable) {
                        throwable.printStackTrace();
                    }
                });


    }

    public UserFace getRegisteredInfo(float[] feature, float feature_flen) {
        if (feature_flen == 0) {
            return null;
        }
        float cv_max = Float.MIN_NORMAL;
        UserFace userFace_max = null;

        Log.d(TAG, "getRegisteredInfo: size()=" + FaceDataUtil.userFaces_all.size());
        synchronized (FaceDataUtil.userFaces_all) {
            for (Iterator<UserFace> iterator = FaceDataUtil.userFaces_all.iterator(); iterator.hasNext(); ) {
                UserFace userFace = iterator.next();
                float[] features = byteArr2floatArr(userFace.getFeature());


                if (features.length != Common.REAL_SENSE_FACE_FEATURE_LENGTH && features.length != Common.SENSE_TIME_FACE_FEATURE_LENGTH) {
                    continue;
                }
                if (FaceDataUtil.GetVectorLength(features) == 0) {
                    continue;
                }


                float cv = FaceDataUtil.calcCosValue_accel(feature, FaceDataUtil.GetVectorLength(feature), features, FaceDataUtil.GetVectorLength(features), features.length);
                if (cv > cv_max) {
                    cv_max = cv;
                    userFace_max = userFace;
                }
            }
        }
        Log.d(TAG, "getRegisteredInfo: cv_max=" + cv_max);
        float threshold = DemoConfig.getInstance().feature_threshold;
        if (userFace_max != null && cv_max > threshold) {
            userFace_max.setSimilarity(cv_max);
            return userFace_max;
        }
        return null;
    }
}
